/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// r_renderInteraction.cpp - ...
//


#include "r_local.h"


/*
 ==============================================================================

 INTERACTION RENDER PATHS

 ==============================================================================
*/


/*
 ==================
 RB_DrawSingleInteraction_Generic
 ==================
*/
static void RB_DrawSingleInteraction_Generic (const drawInteraction_t *di){

	interactionParms_t	*parms;

	// Bind the program
	GL_BindProgram(rg.interactionPrograms[INTERACTION_GENERIC][backEnd.light->l.type]);

	// Set up the program uniforms
	parms = &backEnd.interactionParms[INTERACTION_GENERIC][backEnd.light->l.type];

	R_UniformVector3(parms->viewOrigin, backEnd.localParms.viewOrigin);

	if (backEnd.light->l.type != RL_DIRECTIONAL)
		R_UniformVector3(parms->lightOrigin, backEnd.localParms.lightOrigin);
	else
		R_UniformVector3(parms->lightDirection, backEnd.localParms.lightDirection);

	if (backEnd.light->l.type == RL_CUBIC)
		R_UniformMatrix3(parms->lightAxis, GL_TRUE, backEnd.localParms.lightAxis);

	R_UniformMatrix4(parms->bumpMatrix, GL_TRUE, di->bumpMatrix);
	R_UniformMatrix4(parms->diffuseMatrix, GL_TRUE, di->diffuseMatrix);
	R_UniformMatrix4(parms->specularMatrix, GL_TRUE, di->specularMatrix);
	R_UniformMatrix4(parms->lightMatrix, GL_TRUE, backEnd.localParms.lightMatrix);

	if (backEnd.light->l.type != RL_DIRECTIONAL)
		R_UniformMatrix4(parms->shadowMatrix, GL_TRUE, backEnd.localParms.shadowMatrix);
	else
		R_UniformMatrix4Array(parms->shadowMatrix, MAX_SHADOW_SPLITS, GL_TRUE, backEnd.localParms.shadowMatrixArray);

	R_UniformVector2(parms->colorScaleAndBias, di->colorScaleAndBias);
	R_UniformVector3(parms->diffuseColor, di->diffuseColor);
	R_UniformVector3(parms->specularColor, di->specularColor);
	R_UniformVector2(parms->specularParms, di->specularParms);
	R_UniformVector3(parms->lightColor, di->lightColor);

	if (backEnd.light->l.type == RL_PROJECTED || backEnd.light->l.type == RL_DIRECTIONAL)
		R_UniformVector4(parms->lightPlane, backEnd.localParms.lightPlane);

	R_UniformFloat2(parms->depthScaleAndBias, -backEnd.shadowMap.projectionMatrix[2][2], backEnd.shadowMap.projectionMatrix[3][2]);

	if (backEnd.light->l.type == RL_DIRECTIONAL){
		R_UniformVector3(parms->shadowDistanceFade, backEnd.viewParms.shadowDistanceFade);
		R_UniformVector2Array(parms->shadowSplitScales, MAX_SHADOW_SPLITS, backEnd.shadowMap.splitScales);
	}

	if (!backEnd.shadowMap.available || (backEnd.material->flags & MF_UNSHADOWED)){
		R_UniformFloat4(parms->shadowParms, 0.0f, 0.0f, 0.0f, 0.0f);
		R_UniformFloat4(parms->jitterParms, 0.0f, 0.0f, 0.0f, 0.0f);
	}
	else {
		R_UniformVector4(parms->shadowParms, backEnd.shadowMap.shadowParms);
		R_UniformVector4(parms->jitterParms, backEnd.shadowMap.jitterParms);
	}

	// Bind the textures
	GL_BindMultitexture(di->bumpTexture, TMU_BUMP);
	GL_BindMultitexture(di->diffuseTexture, TMU_DIFFUSE);
	GL_BindMultitexture(di->specularTexture, TMU_SPECULAR);
	GL_BindMultitexture(di->lightProjectionTexture, TMU_LIGHTPROJECTION);
	GL_BindMultitexture(di->lightFalloffTexture, TMU_LIGHTFALLOFF);

	if (backEnd.light->l.type == RL_CUBIC)
		GL_BindMultitexture(di->lightCubeTexture, TMU_LIGHTCUBE);

	GL_BindMultitexture(backEnd.shadowMap.shadowTexture, TMU_SHADOW);
	GL_BindMultitexture(backEnd.shadowMap.jitterTexture, TMU_JITTER);

	// Draw it
	RB_DrawElementsWithCounters(&rg.pc.interactionIndices, &rg.pc.interactionVertices);
}


/*
 ==============================================================================

 AMBIENT INTERACTION RENDER PATHS

 ==============================================================================
*/


/*
 ==================
 RB_DrawSingleAmbientInteraction_Generic
 ==================
*/
static void RB_DrawSingleAmbientInteraction_Generic (const drawInteraction_t *di){

	ambientLightParms_t	*parms;

	// Bind the program
	GL_BindProgram(rg.ambientLightPrograms[AMBIENT_GENERIC]);

	// Set up the program uniforms
	parms = &backEnd.ambientLightParms[AMBIENT_GENERIC];

	R_UniformMatrix4(parms->bumpMatrix, GL_TRUE, di->bumpMatrix);
	R_UniformMatrix4(parms->diffuseMatrix, GL_TRUE, di->diffuseMatrix);
	R_UniformMatrix4(parms->lightMatrix, GL_TRUE, backEnd.localParms.lightMatrix);
	R_UniformVector2(parms->colorScaleAndBias, di->colorScaleAndBias);
	R_UniformVector3(parms->diffuseColor, di->diffuseColor);
	R_UniformVector3(parms->lightColor, di->lightColor);

	// Bind the textures
	GL_BindMultitexture(di->bumpTexture, TMU_BUMP);
	GL_BindMultitexture(di->diffuseTexture, TMU_DIFFUSE);
	GL_BindMultitexture(di->lightProjectionTexture, TMU_LIGHTPROJECTION);
	GL_BindMultitexture(di->lightFalloffTexture, TMU_LIGHTFALLOFF);

	// Draw it
	RB_DrawElementsWithCounters(&rg.pc.interactionIndices, &rg.pc.interactionVertices);
}


// ============================================================================


/*
 ==================
 RB_DrawSingleInteraction
 ==================
*/
void RB_DrawSingleInteraction (drawInteraction_t *di){

	// Development tools
	if (r_skipBump->integerValue)
		di->bumpTexture = NULL;
	if (r_skipDiffuse->integerValue)
		di->diffuseTexture = NULL;
	if (r_skipSpecular->integerValue)
		di->specularTexture = NULL;

	// Make sure we always have a bump stage
	if (di->bumpTexture == NULL){
		di->bumpMatrix.Identity();
		di->bumpTexture = rg.flatTexture;
	}

	// Make sure we always have a diffuse stage
	if (di->diffuseTexture == NULL){
		di->diffuseColor.Set(1.0f);
		di->diffuseMatrix.Identity();
		di->diffuseTexture = rg.whiteTexture;
	}

	// Make sure we always have a specular stage
	if (di->specularTexture == NULL){
		di->specularColor.Set(1.0f);
		di->specularParms.Set(16.0f, 2.0f);
		di->specularMatrix.Identity();
		di->specularTexture = rg.blackTexture;
	}

	// Dispatch to the appropriate render path
	if (backEnd.lightMaterial->lightType == LT_AMBIENT){
		RB_DrawSingleAmbientInteraction_Generic(di);
		return;
	}

	RB_DrawSingleInteraction_Generic(di);
}